package models

import (
	"gorm.io/gorm"
	"strconv"
)

type Roles struct {
	gorm.Model
	Name       string `json:"Name" gorm:"comment:角色名称;unique"`
	Describe   string `json:"describe" gorm:"comment:角色描述;unique"`
	Creator    *Users `json:"creatorID" gorm:"comment:创建者;foreignKey:CreatorID" `
	CreatorID  uint
	Modifier   *Users `json:"modifierID" gorm:"comment:更新者;foreignKey:ModifierID"`
	ModifierID uint
	Menus      []Menus `gorm:"many2many:roles_menus;"`
	Users      []Users `gorm:"many2many:users_roles;"`
	IsUsable   uint8   `json:"isUsable" gorm:"comment:是否启用1:启用,0:禁用"`
}
type RoleTree struct {
	Title    string      `json:"title"`
	Id       uint        `json:"id"`
	Children []*RoleTree `json:"children"`
}

func init() {
	err := DB.AutoMigrate(&Roles{})
	if err != nil {
		return
	}
}

func GetRolesList(name string, page, limit int) (rs []Roles, count int64, err error) {
	if name != "" {
		err = DB.Preload("Creator").Preload("Modifier").Order("created_at asc").Limit(limit).Offset((page-1)*limit).Find(&rs, "name like ? ", "%"+name+"%").Count(&count).Error
		return
	}
	err = DB.Preload("Creator").Preload("Modifier").Order("created_at asc").Limit(limit).Offset((page - 1) * limit).Find(&rs).Count(&count).Error
	return
}

func RoleTreeData(r *RoleTree, menuData []Menus) []*RoleTree {
	for _, v := range menuData {
		if v.ParentID == r.Id {
			childRoleTree := &RoleTree{
				Id:       v.ID,
				Title:    v.Name,
				Children: []*RoleTree{},
			}
			RoleTreeData(childRoleTree, menuData)
			r.Children = append(r.Children, childRoleTree)
		}
	}
	return r.Children
}

func QueryMenuIdById(id uint) (ids []uint, err error) {
	var roles Roles
	err = DB.Preload("Menus", func(db *gorm.DB) *gorm.DB {
		return db.Where("type = ?", 2)
	}).Take(&roles, id).Error
	if err != nil {
		return
	}
	for _, v := range roles.Menus {
		ids = append(ids, v.ID)
	}
	return
}

func EditRolesMenusById(roleID int, menuIDs []int, assetsIds []int, roles Roles) error {
	var role Roles
	var menus []Menus
	// 根据角色ID查询角色
	if err := DB.First(&role, roleID).Error; err != nil {
		return err
	}
	if len(menuIDs) != 0 {
		// 根据菜单ID数组查询菜单
		if err := DB.Find(&menus, menuIDs).Error; err != nil { //这里有个坑，如果menuIDs 为空，他会查询所有的菜单 所有这里要加个判断
			return err
		}
	}
	err := DB.Transaction(func(tx *gorm.DB) error {
		// 跟新角色字段
		if err := tx.Model(&roles).Where("id = ?", roleID).Updates(roles).Error; err != nil {
			return err
		}
		// 更新角色的菜单权限
		if err := tx.Model(&role).Association("Menus").Replace(menus); err != nil {
			return err
		}
		// 删除Casbin 规则
		if err := DelCasbinByRolesId(strconv.Itoa(roleID)); err != nil {
			return err
		}
		if len(assetsIds) != 0 { //如果菜单ids不会空才去执行添加规则
			//添加Casbin 规则
			if err := AddCasbin(strconv.Itoa(roleID), assetsIds); err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return err
	}
	return nil
}

// GetRoles 获取全部角色信息
func GetRoles() (r []Roles, err error) {
	err = DB.Order("created_at asc").Find(&r).Error
	return
}

// GetRolesByIds 根据ids获取角色信息
func GetRolesByIds(ids []int) (roles []Roles, err error) {
	err = DB.Find(&roles, ids).Error
	return
}

// DelRolesById 根据ID删除角色
func DelRolesById(roleID int) error {
	// 根据角色ID查询角色
	var role Roles
	if err4 := DB.First(&role, roleID).Error; err4 != nil {
		return err4
	}
	err := DB.Transaction(func(tx *gorm.DB) error {
		if err1 := tx.Delete(&role, roleID).Error; err1 != nil {
			return err1
		}
		// 清除角色和菜单的关联
		if err2 := tx.Model(&role).Association("Menus").Replace([]Menus{}); err2 != nil {
			return err2
		}
		// 清除角色和用户的关联
		if err3 := tx.Model(&role).Association("Users").Replace([]Users{}); err3 != nil {
			return err3
		}
		// 删除Casbin 规则
		if err4 := DelCasbinByRolesId(strconv.Itoa(roleID)); err4 != nil {
			return err4
		}
		return nil
	})
	if err != nil {
		return err
	}
	return nil
}
func (r *Roles) CreateRoles(menusIds, assetsIds []int) error {
	var mneus []Menus
	DB.Find(&mneus, menusIds)
	r.Menus = mneus
	//return DB.Create(r).Error
	err := DB.Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(r).Error; err != nil {
			return err
		}
		if len(assetsIds) != 0 { //如果菜单ids不会空才去执行添加规则
			//添加Casbin 规则
			if err := AddCasbin(strconv.Itoa(int(r.ID)), assetsIds); err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return err
	}
	return nil
}
